package panel.control;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;

import panel.event.PropertyChangedEvent;
import panel.event.PropertyChangedListener;


public class ValuePanel extends JPanel {
	protected PropertyChangedListener propertyControl;
	
	protected List<ValueField> 	fieldList;
	protected JLabel 			title;
	
	protected GridBagConstraints 	c;

	/*
	 * The ValuePanel is a panel with several text fields attached to
	 * up/down buttons (could have used JSpinner, but I did not
	 * think about it at the time).
	 * 
	 * Each field has a property and an index.
	 * When activated (button clicked or text field 'Enter'ed), an event
	 * will be sent to the property changed listener containing the
	 * property, index, operation (CHANGE or SET) and value.
	 */
	
	public ValuePanel( String name, PropertyChangedListener propertyControl ) {
		setLayout(new GridBagLayout());
		setOpaque(false);

		this.propertyControl = propertyControl;
		
		c = new GridBagConstraints();
		c.insets 	= new Insets(0, 0, 5, 0);
		c.fill 		= GridBagConstraints.BOTH;
		c.gridx 	= GridBagConstraints.RELATIVE;
		c.gridy 	= 0;
		c.weighty 	= 1.0;
		
		title = new JLabel(name);
		c.gridwidth = 3;
		add(title, c);
		
		c.gridwidth = 1;
		c.insets.top 	= 1;
		c.insets.bottom = 1;
		c.gridy++;
		
		fieldList = new ArrayList<ValueField>();
	}
	
	public void addField( String name, int property, int index, double step ) {
		ValueField field = new ValueField(name, step, property, index, propertyControl);
		fieldList.add( field );
		
		c.insets.right	= 5;
		c.weightx 		= 0.2;
		add( field.label, c );
		
		c.insets.right	= 0;
		c.weightx 		= 0.7;
		add( field.text, c );
		
		c.weightx = 0.1;
		add( field.more, c );
		add( field.less, c );
		
		c.insets.top 	= 1;
		c.insets.bottom = 1;
		c.gridy++;
	}
	
	public void addField( String name, int property, int index ) {
		addField( name, property, index, 0.1 );
	}
	
	public void addField( String name[], int property, int index[], double step ) {
		if (name.length != index.length) {
			System.out.println("[ValuePanel] ERROR: name and index list sizes do not match");
			return;
		}
		
		for (int i = 0; i < name.length; i++)
			addField(name[i], property, index[i], step);
	}
	
	public void addField( String name[], int property, int index[] ) {
		addField( name, property, index, 0.1 );
	}
	
	public void addSpace() {
		add( Box.createRigidArea(new Dimension(0,10)), c);
		c.gridy++;
	}
	
	public void setData( HasProperties entity ) {
		ValueField field;
		
		for (int i = 0; i < fieldList.size(); i++) {
			field = fieldList.get(i);
			field.setValue(entity.getProperty(field.index, field.property));
		}
	}
	
	public void setEnabled( boolean enableButton, boolean enableText ) {
		title.setEnabled(enableButton || enableText);
		for (ValueField f: fieldList)
			f.setEnabled(enableButton, enableText);
	}
	
	/* ------------------------------------------------
	 */
	
	protected static class ValueField implements ActionListener {
		PropertyChangedListener propertyControl;
		final int index;
		final int property;
		final double step;
		
		JLabel label;
		JTextField text;
		BasicArrowButton more, less;
		
		static final int TEXTFIELD_WIDTH = 50;
		static final int TEXTFIELD_HEIGHT = 20;
		
		public ValueField( String name, double step, int property, int index, PropertyChangedListener propertyControl ) {
			this.propertyControl = propertyControl;
			this.index = index;
			this.property = property;
			this.step = step;
			
			label = new JLabel(name);
			
			text = new JTextField();
			text.setPreferredSize(new Dimension(TEXTFIELD_WIDTH, TEXTFIELD_HEIGHT));
			text.setBorder(new LineBorder(Color.black));
			text.setActionCommand( String.valueOf(property) );
			text.addActionListener( this );
			
			// This will prevent invalid values and limit to 2 decimal digits
			((AbstractDocument) text.getDocument()).setDocumentFilter( new DocumentFilter() {
				@Override
				public void insertString(FilterBypass fb, int offset,
						String string, AttributeSet attr)
						throws BadLocationException {
					
					replace(fb, offset, 0, string, attr);				
				}

				@Override
				public void replace(FilterBypass fb, int offset, int length,
						String text, AttributeSet attrs)
						throws BadLocationException {

					text = text.replaceAll("\\p{Alpha}", "");
					
					int oldLength = fb.getDocument().getLength();
					String oldString = fb.getDocument().getText(0, oldLength);
					String newString = oldString.substring(0, offset)
										+ text + oldString.substring(offset+length, oldLength);

					DecimalFormatSymbols dfs = new DecimalFormatSymbols();
					int dotIndex = newString.indexOf(dfs.getDecimalSeparator() );
					if (dotIndex != -1 && newString.length() - dotIndex > 3)
						return;
					
					try {
						Double.valueOf(newString);
						super.replace(fb, offset, length, text, attrs);
					}
					catch (NumberFormatException e) {}	
				}
			});

			more = new BasicArrowButton( BasicArrowButton.NORTH );
			more.setActionCommand( String.valueOf(property) );
			more.addActionListener(this);
			
			less = new BasicArrowButton( BasicArrowButton.SOUTH );
			less.setActionCommand( String.valueOf(property) );
			less.addActionListener(this);
		}
		
		public void setValue( double value ) {
			text.setText(String.format("%.2f", value));
		}
		
		public double getValue() {
			String textValue = text.getText();
			if (textValue != null && textValue.length() > 0)
				return Double.parseDouble(textValue);
			
			return 0.0;
		}

		public void actionPerformed(ActionEvent e) {
			double value;
			int op;
			
			if (e.getSource() == text) {
				op = PropertyChangedEvent.SET;
				value = getValue();
			}
			else if (e.getSource() == more) {
				op = PropertyChangedEvent.MOVE;
				value = step;
			}
			else if (e.getSource() == less) {
				op = PropertyChangedEvent.MOVE;
				value = -step;
			}
			else return;
			
			propertyControl.propertyChanged(new PropertyChangedEvent(property, index, op, value));
		}
		
		public void setEnabled( boolean enableButton, boolean enableText ) {
			label.setEnabled(enableButton || enableText);
			more.setEnabled(enableButton);
			less.setEnabled(enableButton);
			
			text.setEnabled(enableText);
			if (!enableText) {
				text.setText("");
				text.setBackground(Color.lightGray);
			}
			else {
				text.setBackground(Color.white);
			}
		}
	}
}
